package org.jeecg.modules.system.controller.login;

import cn.hutool.core.util.RandomUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.login.LoginService;
import org.jeecg.common.login.parameter.DoubleFactorAuthParameter;
import org.jeecg.common.login.parameter.DoubleFactorExecuteParameter;
import org.jeecg.common.login.parameter.OriginalParameter;
import org.jeecg.common.message.UnicomSmsUtil;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.Md5Util;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.config.thirdapp.ThirdAppConfig;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.controller.login.constant.Prompt;
import org.jeecg.modules.system.controller.login.constant.SmsTemplate;
import org.jeecg.modules.system.entity.SysThirdAccount;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Date;

/**
 * 登录Service 实现类
 *
 * @author tianyi.jiang
 * @since 1.0.0
 */
@Slf4j
@Component
public class LoginServiceImpl extends LoginHandler implements LoginService {

    @Autowired
    private ISysThirdAccountService sysThirdAccountService;

    @Override
    public Result<?> authByThirdAccount(String thirdId, String type) {
        SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneByThirdUserId(thirdId, type);
        if (sysThirdAccount != null) {
            String sysUserId = sysThirdAccount.getSysUserId();
            if (StringUtils.isNotEmpty(sysUserId)) {
                SysUser sysUser = sysUserService.getById(sysUserId);
                // 校验用户是否有效
                Result<?> result = sysUserService.checkUserIsEffective(sysUser);
                if (!result.isSuccess()) {
                    return result;
                }
                return this.doLogin(sysUser);
            }
        }
        return Result.error(Prompt.AUTHENTICATION_FAILED);
    }

    @Override
    public Result<?> authByMobile(String mobile) {
        SysUser sysUser = sysUserService.getUserByPhone(mobile);
        if (sysUser == null) {
            return Result.error(Prompt.AUTHENTICATION_FAILED);
        }
        // 校验用户是否有效
        Result<?> result = sysUserService.checkUserIsEffective(sysUser);
        if (!result.isSuccess()) {
            return result;
        }
        return this.doLogin(sysUser);
    }

    @Override
    public Result<?> auth4WechatMiniProgram(String thirdId, String type) {
        if (StringUtils.isEmpty(thirdId)) {
            return Result.error(Prompt.OPEN_ID_FAILED);
        }
        SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneByThirdUserId(thirdId, ThirdAppConfig.WECHAT_MINI_PROGRAM);
        if (sysThirdAccount != null) {
            String sysUserId = sysThirdAccount.getSysUserId();
            SysUser sysUser = sysUserService.getById(sysUserId);
            // 校验用户是否有效
            Result<?> result = sysUserService.checkUserIsEffective(sysUser);
            if (!result.isSuccess()) {
                return result;
            }
            return this.doLogin(sysUser);
        }
        // 返回加密Open Id
        return Result.OK(rsaEncryptProperty.encrypt(thirdId));
    }

    @Override
    public Result<?> register4WechatMiniProgram(String thirdId, String mobile) {
        // 解密Open Id
        String openId = rsaEncryptProperty.decrypt(thirdId);
        if (StringUtils.isNotEmpty(mobile)) {
            String userId = sysUserService.thirdAuth(openId, mobile, ThirdAppConfig.WECHAT_MINI_PROGRAM);
            SysUser sysUser = sysUserService.getById(userId);
            // 校验用户是否有效
            Result<?> result = sysUserService.checkUserIsEffective(sysUser);
            if (!result.isSuccess()) {
                return result;
            }
            return this.doLogin(sysUser);
        }
        return Result.error(Prompt.MINI_PROGRAM_PHONE_FAILED);
    }

    @Override
    public Result<?> auth4WechatPublic(String thirdId) {
        SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneByThirdUserId(thirdId, ThirdAppConfig.WECHAT_PUBLIC);
        String sysUserId;
        if (sysThirdAccount == null) {
            sysUserId = sysUserService.thirdAuth(thirdId, null, ThirdAppConfig.WECHAT_PUBLIC);
        } else {
            sysUserId = sysThirdAccount.getSysUserId();
        }
        if (StringUtils.isNotEmpty(sysUserId)) {
            SysUser sysUser = sysUserService.getById(sysUserId);
            // 校验用户是否有效
            Result<?> result = sysUserService.checkUserIsEffective(sysUser);
            if (!result.isSuccess()) {
                return result;
            }
            return this.doLogin(sysUser);
        }
        return Result.error(Prompt.AUTHENTICATION_FAILED);
    }

    @Override
    public Result<?> auth4DingMiniProgram(String mobile) {
        SysUser sysUser = sysUserService.getUserByPhone(mobile);
        if (sysUser == null) {
            throw new JeecgBootException("登录失败，用户不存在！");
        }
        return this.doLogin(sysUser);
    }

    @Override
    public Result<?> getDoubleFactorParameter(OriginalParameter originalParameter) {
        Result<?> result = this.routineVerification(originalParameter);
        if (result.isSuccess()) {
            SysUser sysUser = (SysUser) result.getResult();
            // 判定验证码, 10分钟内有效
            String signature = JWT.create().withClaim("username", sysUser.getUsername()).withExpiresAt(new Date()).sign(Algorithm.HMAC256(sysUser.getPassword()));
            String key = this.redisKey(sysUser.getUsername());
            Object object = redisUtil.get(key);
            if (object != null) {
                return Result.error(Prompt.SMS_CODE_EFFECTIVE_PROMPT, signature);
            }
            // 短信验证码（6随机数）
            String captcha = RandomUtil.randomNumbers(6);
            // 内容
            String content = String.format(SmsTemplate.LOGIN_SMS_TEMPLATE, captcha);
            DoubleFactorExecuteParameter doubleFactorExecuteParameter = new DoubleFactorExecuteParameter();
            doubleFactorExecuteParameter.setMobile(sysUser.getPhone());
            doubleFactorExecuteParameter.setMail(sysUser.getEmail());
            doubleFactorExecuteParameter.setKey(key);
            doubleFactorExecuteParameter.setCaptcha(captcha);
            doubleFactorExecuteParameter.setContent(content);
            doubleFactorExecuteParameter.setSignature(signature);
            return Result.OK(doubleFactorExecuteParameter);
        }
        return result;
    }

    private String redisKey(String username) {
        return Md5Util.md5Encode("DF" + username, null);
    }

    @Override
    public Result<?> writeCaptcha4DoubleFactor(String key, String captcha, String signature) {
        // 验证码10分钟内有效
        redisUtil.set(key, captcha, 600);
        return Result.OK(Prompt.CODE_SEND_SUCCESS_PROMPT, signature);
    }

    @Override
    public Result<?> auth4DoubleFactorParameter(DoubleFactorAuthParameter doubleFactorAuthParameter) {
        String username = JwtUtil.getUsername(doubleFactorAuthParameter.getSignature());
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername, username);
        SysUser sysUser = sysUserService.getOne(queryWrapper);

        // 签名十分钟内有效
        Date date = JWT.decode(doubleFactorAuthParameter.getSignature()).getExpiresAt();
        if ((System.currentTimeMillis() - date.getTime()) > 10 * 60 * 1000) {
            String signature = JWT.create().withClaim("username", sysUser.getUsername()).withExpiresAt(new Date()).sign(Algorithm.HMAC256(sysUser.getPassword()));
            return Result.error(Prompt.CODE_WRONG_PROMPT, signature);
        }

        // 指定 lua 脚本
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        // 执行 lua 脚本
        String key = this.redisKey(sysUser.getUsername());
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), doubleFactorAuthParameter.getCaptcha());
        if (result == null || result == 0L) {
            Object object = redisUtil.get(key);
            if (object != null) {
                String signature = JWT.create().withClaim("username", sysUser.getUsername()).withExpiresAt(new Date()).sign(Algorithm.HMAC256(sysUser.getPassword()));
                return Result.error(Prompt.CODE_WRONG_PROMPT, signature);
            }
            throw new JeecgBootException("验证码已过期，请重新获取！");
        }
        return this.doLogin(sysUser);
    }

    public LoginServiceImpl(RedisUtil redisUtil, UnicomSmsUtil unicomSmsUtil, RsaEncryptProperty rsaEncryptProperty, BaseCommonService baseCommonService, ISysDictService sysDictService, ISysTenantService sysTenantService, ISysUserService sysUserService, ISysDepartService sysDepartService, RedisTemplate<String, Object> redisTemplate) {
        super(redisUtil, unicomSmsUtil, rsaEncryptProperty, baseCommonService, sysDictService, sysTenantService, sysUserService, sysDepartService, redisTemplate);
    }

}
